package simpledb;

import java.io.Serializable;
import java.util.*;

/**
 * TupleDesc describes the schema of a tuple.
 */
public class TupleDesc implements Serializable {
    private TDItem[]  descAr;//创建数组存储所有TDItem类对象

    private int numFields;
    /**
     * A help class to facilitate organizing the information of each field
     * */
    public static class TDItem implements Serializable {

        private static final long serialVersionUID = 1L;

        /**
         * The type of the field
         * */
        public final Type fieldType;

        /**
         * The name of the field
         * */
        public final String fieldName;

        public TDItem(Type t, String n) {
            this.fieldName = n;
            this.fieldType = t;
        }

        public String toString() {
            return fieldName + "(" + fieldType + ")";
        }
        public boolean equals(Object o) {
            if(this==o){
                return true;
            }
            if(o instanceof TDItem){
                TDItem td = (TDItem) o;
                if(((td.fieldName==null&&fieldName==null)||(td.fieldName.equals(fieldName)))
                        &&(td.fieldType.equals(fieldType))){
                    return true;
                }else return false;
            }else return false;
        }
    }

    /**
     * @return
     *        An iterator which iterates over all the field TDItems
     *        that are included in this TupleDesc
     * */
    public Iterator<TDItem> iterator() {
        // some code goes here
        return new TDIterator();
    }
    private class TDIterator implements Iterator<TDItem> {

        private int pos = 0;

        @Override
        public boolean hasNext() {
            if(descAr.length>pos){
                return true;
            }
            return false;
        }

        public TDItem next() {
            if (hasNext()==false) {
                throw new NoSuchElementException();
            }
            return descAr[pos++];
        }
    }
    private static final long serialVersionUID = 1L;

    /**
     * Create a new TupleDesc with typeAr.length fields with fields of the
     * specified types, with associated named fields.
     *
     * @param typeAr
     *            array specifying the number of and types of fields in this
     *            TupleDesc. It must contain at least one entry.
     * @param fieldAr
     *            array specifying the names of the fields. Note that names may
     *            be null.
     */
    public TupleDesc(Type[] typeAr, String[] fieldAr) {
        // some code goes here
        if(typeAr.length!=fieldAr.length){
            System.out.println("typeAr和fieldAr长度不一致");
            return;
        }
        numFields= typeAr.length;
        descAr = new TDItem[numFields];
        for(int i=0;i<numFields;i++){
            descAr[i]= new TDItem(typeAr[i],fieldAr[i]);
        }
    }

    /**
     * Constructor. Create a new tuple desc with typeAr.length fields with
     * fields of the specified types, with anonymous (unnamed) fields.
     *
     * @param typeAr
     *            array specifying the number of and types of fields in this
     *            TupleDesc. It must contain at least one entry.
     */
    public TupleDesc(Type[] typeAr) {
        numFields= typeAr.length;
        descAr = new TDItem[numFields];
        String str= "noname";
        for(int i=0;i<numFields;i++){
            descAr[i]= new TDItem(typeAr[i],str);
        }
    }

    /**
     * @return the number of fields in this TupleDesc
     */
    public int numFields() {
        // some code goes here
        return this.numFields;
    }

    /**
     * Gets the (possibly null) field name of the ith field of this TupleDesc.
     *
     * @param i
     *            index of the field name to return. It must be a valid index.
     * @return the name of the ith field
     * @throws NoSuchElementException
     *             if i is not a valid field reference.
     */
    public String getFieldName(int i) throws NoSuchElementException {
        // some code goes here
        if(i<0||i>=this.numFields){
            throw new NoSuchElementException();
        }
        return descAr[i].fieldName;

    }

    /**
     * Gets the type of the ith field of this TupleDesc.
     *
     * @param i
     *            The index of the field to get the type of. It must be a valid
     *            index.
     * @return the type of the ith field
     * @throws NoSuchElementException
     *             if i is not a valid field reference.
     */
    public Type getFieldType(int i) throws NoSuchElementException {
        // some code goes here
        if(i<0||i>=this.numFields()){
            throw new NoSuchElementException();
        }
        return descAr[i].fieldType;

    }

    /**
     * Find the index of the field with a given name.
     *
     * @param name
     *            name of the field.
     * @return the index of the field that is first to have the given name.
     * @throws NoSuchElementException
     *             if no field with a matching name is found.
     */
    public int fieldNameToIndex(String name) throws NoSuchElementException {
        // some code goes here
        if (name == null) {
            throw new NoSuchElementException();
        }
        for(int i=0;i<this.numFields();i++){
            if(descAr[i].fieldName!=null&&descAr[i].fieldName.equals(name)){
                return i;
            }
        }
        throw new NoSuchElementException();

    }

    /**
     * @return The size (in bytes) of tuples corresponding to this TupleDesc.
     *         Note that tuples from a given TupleDesc are of a fixed size.
     */
    public int getSize() {
        // some code goes here
        int size=0;
        for(int i=0;i<this.numFields;i++){
            size+=descAr[i].fieldType.getLen();
        }
        return size;
    }

    /**
     * Merge two TupleDescs into one, with td1.numFields + td2.numFields fields,
     * with the first td1.numFields coming from td1 and the remaining from td2.
     *
     * @param td1
     *            The TupleDesc with the first fields of the new TupleDesc
     * @param td2
     *            The TupleDesc with the last fields of the TupleDesc
     * @return the new TupleDesc
     */
    public static TupleDesc merge(TupleDesc td1, TupleDesc td2) {
        // some code goes here
        int len1 = td1.numFields;
        int len2 = td2.numFields;
        int len = len1+len2;
        Type[] typeAr = new Type[len];
        String[] fieldAr = new String[len];
        for(int i=0;i<len1;i++){
            typeAr[i]=td1.getFieldType(i);
            fieldAr[i]=td1.getFieldName(i);
        }
        for(int i=0;i<len2;i++){
            typeAr[i+len1]=td2.getFieldType(i);
            fieldAr[i+len1]=td2.getFieldName(i);
        }
        return new TupleDesc(typeAr,fieldAr);

    }

    /**
     * Compares the specified object with this TupleDesc for equality. Two
     * TupleDescs are considered equal if they have the same number of items
     * and if the i-th type in this TupleDesc is equal to the i-th type in o
     * for every i.
     *
     * @param o
     *            the Object to be compared for equality with this TupleDesc.
     * @return true if the object is equal to this TupleDesc.
     */

    public boolean equals(Object o) {
        // some code goes here
        if (this == o) {
            return true;
        }
        if(o instanceof TupleDesc){
            TupleDesc td = (TupleDesc) o;
            if(td.numFields!=this.numFields){
                return false;
            }
            int len = this.numFields;
            for(int i=0;i<len;i++){
                if(!descAr[i].equals(td.descAr[i]))
                    return false;
            }
            return true;
        }else return false;

    }

    public int hashCode() {
        // If you want to use TupleDesc as keys for HashMap, implement this so
        // that equal objects have equals hashCode() results
        throw new UnsupportedOperationException("unimplemented");
    }

    /**
     * Returns a String describing this descriptor. It should be of the form
     * "fieldType[0](fieldName[0]), ..., fieldType[M](fieldName[M])", although
     * the exact format does not matter.
     *
     * @return String describing this descriptor.
     */
    public String toString() {
        // some code goes here
        StringBuffer str = new StringBuffer();
        str.append("Fields: ");
        int len = this.numFields();
        for(int i=0;i<len;i++){
            str.append(this.descAr[i].toString());
        }
        String s = str.toString();
        return s;

    }
}
